在過去的一個禮拜裡,我們了解了很多程式中的一些基本概念與術語,這讓我們了解了一些程式的基本運作的概念。到目前為止我們所了解的程式就是把一組指令排在一起,雖然它能跟著指令做事,卻沒有應對不同的狀況的能力。因此,從今天開始,就讓我們進一步了解程式設計架構中非常重要的一環,陳述式 (Statements),來讓我們所編寫的程式有更豐富的變化吧。首先,我們會討論的是陳述式中最常被使用的條件陳述式。不過,在開始之前,讓我們先簡單了解一下什麼是陳述式吧。
陳述式就是電腦的任何動作,例如在之前提過的宣告變數的行為便是陳述式的一種了。不過,這樣子不就是跟運算式一樣嗎?實際上,並沒有一個很清晰的界線去區分陳述式與運算式。從一般的看法來說,我們會根據程式實際執行的行為來定義它到底是陳述式還是運算式,根據維基百科中的記載便認為:陳述式的目的是 「執行」(execute) ;而運算式的目的是 「求值」(evaluate)。因此會把是否直接影響並回傳資料或變數的值這一件事來區分這個指令是陳述式還是運算式。不過,當我們看微軟的官方文檔時,會發現運算式被歸類為陳述式的一項分支 [運算式陳述式 (Expression Statements)]。
因此,我們可以把兩者的關係理解為:程式執行的所有行為都是陳述式,而陳述式中會回傳資料或變數的值的行為則會被稱為運算式。不過,當兩者同時被提及的情況中,我們也可以更簡單地把兩者以二分法來理解:運算式是會回傳資料或變數的值的行為、而陳述式則是不會改變資料或變數的所有其他行為。
延伸閱讀:
不知道大家有沒有聽說過一個笑話:
(來源自:CodingBar 臉書專頁)
「親愛的,你可以去幫我買5顆蘋果嗎?如果有橘子的話,就買2顆。」
然後不具名工程師買了2顆蘋果回家。
「為什麼你買2顆蘋果?」
「因為攤販有賣橘子啊!」
可能大家會看不懂這個笑話為什麼不具名工程師會買了2顆蘋果回家,那是因為他隱藏了 條件陳述式 (Conditional Statements) 的概念在裡面。簡單的形容條件陳述式的話,它就是在執行指令前加上一個條件,只有在達到指定的條件時程式才會執行該指令。條件陳述式一般有兩種應用的方式,分別為 if-else 陳述式 (If-else Statement) 跟 switch 陳述式 (Switch Statement)。今天,我們會先討論 if-else 陳述式。
If-else 陳述式的概念是嘗試把我們在執行程式時可能會面對的狀況以二分法來應對:如果 (if) 發生了狀況 A,就要執行指令 A;否則 (else),便執行指令 B。而這些狀況我們會以條件 (Conditions) 來表達,條件一般是由回傳布林值資料的比較及邏輯運算式所組成,需要注意的是,一項條件不一定只有使用一項運算式,它可以是由多項運算式所混合組成的複合運算式,但一項條件最終只會回傳一個布林值資料來進行檢查。
一般 if-else 陳述式的架構如下:
如果[if] 條件為真 (True) 時:
執行 指令組 (陳述式) A
否則[else]:(即條件為假 (False) 時)
執行 指令組 (陳述式) B
以下是一個很簡單的例子:[Python]
numA = input("數字 A 為:") # 讓使用者輸入數字 A
numB = input("數字 B 為:") # 讓使用者輸入數字 B
if numA > numB: # 如果 [數字 A 比數字 B 大] 時:
print("數字 A 較大!") # 顯示:數字 A 較大!
else: # 否則:
print("數字 B 較大!") # 顯示:數字 B 較大!
在上面的例子中,我們讓使用者輸入兩組數字,並比較兩者的大小,當數字 A 比較大時程式便顯示「數字 A 較大!」,而當數字 B 比較大時程式則顯示「數字 B 較大!」。這個例子非常簡單,因此沒有問題... 嗎? 很快我們便發現當輸入的兩組數字一樣大時,程式還是顯示「數字 B 較大!」,這是因為在上面的例子中我們只檢查了數字 A 是否比數字 B 大的條件,可是實際上除了數字 A 較大跟數字 B 較大的狀況外,還有數字 A 跟數字 B 一樣大的第三個狀況存在。因此我們還需要再多檢查一項條件,那應該怎樣做呢?這時候我們可以考慮使用巢狀 if-else 陳述式 (Nested If-else Statements)。
巢狀 if-else 陳述式是指在 if-else 陳述式中再檢查了條件後執行指令時,再度檢查新的條件來決定要執行的指令。不過,不一定要在 if 跟 else 的部分都執行新的 if-else 陳述式才是巢狀 if-else 陳述式,就算只有一邊有執行新的 if-else 陳述式也會被算為巢狀 if-else 陳述式。
簡單的巢狀 if-else 陳述式的例子架構如下:
如果[if] 條件 1 為真 (True) 時:
如果[if] 條件 2 為真 (True) 時:
執行 指令組 (陳述式) A
否則[else]:(即條件 1 為真, 條件 2 為假 (False) 時)
執行 指令組 (陳述式) B
否則[else]:(即條件 1 為假 (False) 時)
如果[if] 條件 3 為真 (True) 時:
執行 指令組 (陳述式) C
否則[else]: (即條件 1 為假, 條件 3 為假 (False) 時)
執行 指令組 (陳述式) D
因此,我們可以把上面的例子改為:[Python]
numA = input("數字 A 為:") # 讓使用者輸入數字 A
numB = input("數字 B 為:") # 讓使用者輸入數字 B
if numA > numB: # 如果 [數字 A 比數字 B 大] 時:
print("數字 A 較大!") # 顯示:數字 A 較大!
else: # 否則:
if numA == numB: # 如果 [數字 A 跟數字 B 一樣大] 時:
print("數字 A 跟數字 B 一樣大!") # 顯示:數字 A 跟數字 B 一樣大!
else: # 否則:
print("數字 B 較大!") # 顯示:數字 B 較大!
這樣,我們便改善了之前在數字 A 跟數字 B 一樣大時錯誤顯示「數字 B 較大!」的問題了。可是,如果這次我們遇到更多的狀況跟條件時又該怎麼辦呢?難道要在巢狀 if-else 陳述式中不斷追加更多的 if-else 陳述式嗎?
無限巢狀 if-else 陳述式的架構:
如果[if] 條件 1 為真 (True) 時:
執行 指令組 (陳述式) A
否則[else]:
如果[if] 條件 2 為真 (True) 時:
執行 指令組 (陳述式)B
否則[else]:
如果[if] 條件 3 為真 (True) 時:
執行 指令組 (陳述式)C
否則[else]:
如果[if] 條件 4 為真 (True) 時:
執行 指令組 (陳述式)D
否則[else]:
...
天啊! 這也太複雜了吧! 因此便有人為了需要檢查多項條件的狀況設計了一個簡化版寫法 if-else if-else 陳述式 (If-else if-else Statement)。
當我們再次回顧無限巢狀 if-else 陳述式的架構時,我們會發現除了最後的一次 else 的部分中是會執行指令組 (陳述式) 外,實際上每一層的 else 的部分都是用來開啟新的 if-else 陳述式來檢查新的條件。因此,如果我們把這個巢狀架構抽出來,讓新的檢查條件動作變回 if-else 陳述式中的一部分時,整個架構就會變得更簡潔了。作為新的檢查條件動作中所使用的關鍵字 else if 這個字就是代表了它是「前面 if 條件檢查的 else 的狀況下所開啟的 if 條件檢查動作」。
如果[if] 條件 1 為真 (True) 時:
執行 指令組 (陳述式) A
否則如果[else if] 條件 2 為真 (True) 時:(即條件 1 為假 (False) 時)
執行 指令組 (陳述式) A
否則如果[else if] 條件 3 為真 (True) 時:(即條件 1 跟 2 皆為假 (False) 時)
執行 指令組 (陳述式) A
...
否則[else]:(即上述條件全部為假 (False) 時)
執行 指令組 (陳述式) B
在了解 if-else 陳述式後,當我們再一次看前面的笑話,大家知不知道那位不具名工程師為什麼會買2顆蘋果回家呢?
「親愛的,你可以去幫我買5顆蘋果嗎?如果有橘子的話,就買2顆。」
然後不具名工程師買了2顆蘋果回家。
「為什麼你買2顆蘋果?」
「因為攤販有賣橘子啊!」
最後,就讓我們以簡單的計算機作例子來做今天的結尾吧:[C#]
using System;
public class Calculator
{
public static void Main(string[] args)
{
int numA, numB; // 宣告變數 numA 跟 numB
float sum = 0; // 宣告變數 sum
string opt; // 宣告變數 opt
Console.WriteLine("請輸入數字A:"); // 顯示:"請輸入數字A:"
numA = int.Parse(Console.ReadLine()); // 讀取使用者輸入的數值並在型態變換後儲存在 numA 中
Console.WriteLine("請輸入數字B:"); // 顯示:"請輸入數字B:"
numB = int.Parse(Console.ReadLine()); // 讀取使用者輸入的數值並在型態變換後儲存在 numB 中
Console.WriteLine("請輸入算術符號:(+, -, *, /)"); // 顯示:"請輸入算術符號:(+, -, *, /)"
opt = Console.ReadLine(); // 讀取使用者輸入的值並儲存在 opt 中
if (opt == "+") { // 如果:opt 等於 "+"
sum = numA + numB; // 執行 numA + numB 並把結果儲存在 sum 中
}
else if (opt == "-") { // 否則如果:opt 等於 "-"
sum = numA - numB; // 執行 numA - numB 並把結果儲存在 sum 中
}
else if (opt == "*") { // 否則如果:opt 等於 "*"
sum = numA * numB; // 執行 numA * numB 並把結果儲存在 sum 中
}
else if (opt == "/") { // 否則如果:opt 等於 "/"
sum = (float)numA / numB; // 先讓 numA 執行型態變換轉換成浮點數來確保計算結果不會四捨五入為整數,再執行 numA / numB 並把結果儲存在 sum 中
}
else { // 否則:
Console.WriteLine("算術符號輸入錯誤!"); // 顯示:"算術符號輸入錯誤!"
}
Console.WriteLine("結果為:" + sum.ToString()); // 先讓 sum 執行型態變換轉換成字串,再顯示:"結果為:[計算結果]"
}
}